#!/usr/bin/env python3

import asyncio
import base64
import hashlib
import hmac
import json
import time
from datetime import datetime, timedelta
from pathlib import Path

import cred
import websockets

# Product
# ENDPOINT = 'wss://ws-md.international.coinbase.com'

# Sandbox
ENDPOINT = 'wss://ws-md.n5e2.coinbase.com'


BOOK_DUMP_TMPL = '''
============================================================================================
{product_id:<15} {ask_price:<15} {ask_qty:<15} {ask[trade_price]:<15} {ask[trade_qty]:<15}
--------------------------------------------------------------------------------------------
{product_id:<15} {bid_price:<15} {bid_qty:<15} {bid[trade_price]:<15} {bid[trade_qty]:<15}
============================================================================================
'''


async def ws_feed(num_rcv: int):
    async with websockets.connect(ENDPOINT) as websocket:
        ts_sec = str(int(time.time()))
        signature = hmac.new(
            cred.SECRET,
            f'{ts_sec}{cred.ACCESS_KEY}CBINTLMD{cred.PASSPHRASE}'.encode(),
            hashlib.sha256
        ).digest()
        request = {
            'type': 'SUBSCRIBE',
            'product_ids': ['BTC-PERP', 'ETH-PERP', 'BTC-USDC', 'ETH-USDC'],
            'channels': [
                'MATCH', 'LEVEL1'
            ],
            'time': ts_sec,
            'key': cred.ACCESS_KEY,
            'passphrase': cred.PASSPHRASE,
            'signature': base64.b64encode(signature).decode()
        }
        print(f'SECRET={cred.SECRET}')
        print(f'>>> {json.dumps(request)}')
        await websocket.send(json.dumps(request))

        last_dump_ts = datetime.now()
        books = dict([(product_id, dict(quote={}, trade={}))
                     for product_id in request['product_ids']])

        while num_rcv != 0:
            response = await websocket.recv()
            print(f'<<< {response}')
            num_rcv -= 1

            md = json.loads(response)
            if md.get('channel') == 'LEVEL1':
                books[md['product_id']]['quote'] = md
                ts = datetime.fromisoformat(md['time'].rstrip('Z'))
            elif md.get('channel') == 'MATCH':
                books[md['product_id']]['trade'] = md
                ts = datetime.fromisoformat(md['time'].rstrip('Z'))
            else:
                continue
            if ts - last_dump_ts >= timedelta(seconds=1):
                def format_book(product_id, book):
                    ctx = dict(ask_price=' ', ask_qty=' ', ask=dict(trade_price=' ', trade_qty=''),
                               bid_price=' ', bid_qty=' ', bid=dict(trade_price=' ', trade_qty=''),
                               product_id=product_id)
                    ctx.update(book['quote'])
                    if book['trade'].get('aggressor_side') == 'BUY':
                        ctx['ask'].update(book['trade'])
                    else:
                        ctx['bid'].update(book['trade'])
                    return BOOK_DUMP_TMPL.format(**ctx)
                Path.cwd().joinpath('LEVEL1').write_text(''.join([
                    format_book(product_id, book) for (product_id, book) in books.items()]))
                last_dump_ts = ts


asyncio.get_event_loop().run_until_complete(ws_feed(-1))
